package br.requisiteworks.seguranca.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import br.requisiteworks.modelo.Funcionario;
import br.requisiteworks.modelo.service.FuncionarioService;
import br.requisiteworks.seguranca.modelo.Funcao;
import br.requisiteworks.seguranca.modelo.Usuario;
import br.requisiteworks.seguranca.modelo.UsuarioLogado;
import br.requisiteworks.seguranca.modelo.servico.SecurityService;
import br.requisiteworks.util.persistencia.ErroBancoDados;
import br.requisiteworks.util.persistencia.ResultadoVazio;

@Controller
@Secured("ROLE_ADMINISTRADOR")
@RequestMapping("/usuario")
public class UsuarioController {

	private static final Usuario VAZIO = new Usuario();

	private final Funcao USUARIO = new Funcao("USUARIO");

	@Autowired
	private SecurityService securityService;

	@Autowired
	private FuncionarioService funcionarioService;

	@RequestMapping(method = RequestMethod.GET, value = "/adicionar.do")
	public @ModelAttribute("usuario")
	Usuario adicionar(ModelMap model) {

		List<Funcao> funcoes;
		try {
			funcoes = this.securityService.listarFuncoes();

			funcoes.remove(USUARIO);

			model.addAttribute("funcaoList", funcoes);

		} catch (ResultadoVazio e) {
			model.addAttribute("erro", e.getMessage());

		}

		return VAZIO;
	}

	@RequestMapping(method = RequestMethod.GET, value = "/atualizar.do")
	public @ModelAttribute("usuario")
	Usuario atualizar(@RequestParam("username") String username, ModelMap model) {

		try {
			List<Funcao> funcoes = new LinkedList<Funcao>(securityService
					.listarFuncoes());

			funcoes.remove(USUARIO);

			model.addAttribute("funcaoList", this.securityService
					.listarFuncoes());
			return this.securityService.getUsuario(username);
		} catch (ResultadoVazio e) {
			model.addAttribute("erro", e.getMessage());

		}

		return VAZIO;
	}

	@RequestMapping(method = RequestMethod.POST, value = "/atualizar.do")
	public String atualizar(
			@RequestParam("username") String username,
			@RequestParam(value = "authorities", required = false) Integer[] authorities,
			ModelMap model) {

		try {
			boolean roleUsuario = false;
			Usuario usuario = this.securityService.getUsuario(username);

			if (authorities != null && authorities.length > 0) {
				ArrayList<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(
						authorities.length);

				for (Integer auth : authorities) {

					Funcao funcao = this.securityService.getFuncao(auth);

					if (funcao.equals(USUARIO)) {
						roleUsuario = true;
					}

					auths.add(funcao);

				}

				if (!roleUsuario) {
					Funcao f = this.securityService.getFuncao("USUARIO");
					auths.add(f);
				}

				usuario.setAuthorities(auths);
			}

			usuario.setUsuarioRegistro(UsuarioLogado.getUsername());
			usuario.setDataRegistro(new Date());

			this.securityService.atualizaUsuario(usuario);

			return "redirect:pesquisar.do";
		} catch (ErroBancoDados e) {
			model.addAttribute("erro", e.getMessage());
			return null;
		}
	}

	@RequestMapping(value = "/adicionar.do", method = RequestMethod.POST)
	public String adicionar(@RequestParam("username") String username,
			@RequestParam("password") String password,
			@RequestParam("funcionario.numero") Integer numFuncionario,

			@RequestParam("authorities") Integer[] authorities, ModelMap model) {
		boolean roleUsuario = false;
		try {
			Usuario usuario = new Usuario();

			Funcionario funcionario = this.funcionarioService
					.carregarFuncionario(numFuncionario);

			usuario.setUsername(username);
			usuario.setPassword(password);
			usuario.setFuncionario(funcionario);

			if (authorities != null && authorities.length > 0) {
				ArrayList<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(
						authorities.length);

				for (Integer auth : authorities) {
					Funcao funcao = this.securityService.getFuncao(auth);

					if (funcao.equals(USUARIO)) {
						roleUsuario = true;
					}

					auths.add(funcao);
				}
				if (!roleUsuario) {
					Funcao f = this.securityService.getFuncao("USUARIO");
					auths.add(f);
				}
				usuario.setAuthorities(auths);
			}

			usuario.setUsuarioRegistro(UsuarioLogado.getUsername());
			try {
				this.securityService.salvarUsuario(usuario);
			} catch (ConstraintViolationException e) {
				model.addAttribute("error",
						"Tentativa de cadastrar usuario existente.");
				return null;
			}
		} catch (ResultadoVazio e) {
			model.addAttribute("error", e);
			return null;
		}
		return "redirect:pesquisar.do";
	}

	@RequestMapping(value = "/pesquisar.do", method = RequestMethod.GET)
	public List<Usuario> pesquisar(ModelMap model) {
		try {
			return this.securityService.getUsuarios();
		} catch (ResultadoVazio e) {
			model.addAttribute("error", e);
			return null;
		}
	}

	@RequestMapping(value = "/pesquisar.do", method = RequestMethod.POST)
	public List<Usuario> pesquisarUsuario(@RequestParam("q") String query,
			ModelMap model) {

		try {
			return Arrays.asList(this.securityService.getUsuario(query.trim()));
		} catch (ResultadoVazio e) {
			model.addAttribute("erro", e.getMessage());
			return null;
		}

	}
}
